package com.around.test.m3u8;

import com.around.test.m3u8.util.M3U8Util;
import com.around.test.m3u8.vo.M3U8;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.springframework.util.StringUtils;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;

/**
 * @description:
 * @author: moodincode
 * @create 2021/2/24
 */
public class M3U8Download {


    /**
     * 正式文件存储地址(合并之后的文件)
     */
    private static String tofile = "D:\\test\\tp0\\";
    /**
     * M3U8文件URL的地址,用于批量下载
     */
    private static String m3u8TxtFile="D:\\test\\tp\\n\\test.txt";


    /**
     * 临时文件存储地址(M3U8视频段)
     */
    private static String folderpath = "D:\\test\\tp\\n8";
    /**
     * 下载完的文件所放的文件夹名字
     */
    private static String foldername = "test";

    /**
     * 重试次数,防止下载失败
     */
    public static int RETRY_TIMES = 30000;

    public static void main(String[] args) throws IOException, InterruptedException {
        //排除日志
        excludeLog();
        List<String> urls=new ArrayList<>();
        if(!StringUtils.isEmpty(m3u8TxtFile)){
            //读取txt文件获取多个m3u8文件
            readByTxt(urls);
        }
        if(urls.isEmpty()){
            //通过剪切板获取复制的url
            final String url = getSysClipboardText();
            //https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8
            //
            if (!StringUtils.isEmpty(url) && (url.toLowerCase().startsWith("http://") || url.toLowerCase().startsWith("https://"))) {
                /** m3u8地址 */
                System.out.println("获取到链接地址：" + url);
            } else {
                System.out.println("不合法链接地址！");
                return;
            }
            urls.add(url);
        }
        int size = urls.size();
        if(size >1){
            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                    .setNameFormat("主任务-%d").build();
           ExecutorService mainExecutor = new ThreadPoolExecutor(20, size>20?20:size, 600, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(), namedThreadFactory,
                    new ThreadPoolExecutor.CallerRunsPolicy());

            for (int i = 0; i < size; i++) {
                File file=new File(tofile + i + ".ts");
                if(file.exists()){
                    System.out.println("已存在文件:"+file.getName());
                }
                //设置好初始路径
               String folder= folderpath + File.separator + foldername+ File.separator+i;
               String threadName="子任务"+(i+1);
               //去除问号后面的
                String url = urls.get(i).split("\\?")[0];
                int t=i;
                mainExecutor.execute(()->{
                    //下载单个m3u8文件
                    try {

                        downloadOneM3u8(url,folder,Thread.currentThread().getName()+"-"+threadName,t);
                        //合并完成后删除缓存文件
                        delAllFile(new File(folder));
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });

            }
            mainExecutor.shutdown();
            System.out.println(Thread.currentThread().getName()+" 等待下载中...");
            while (!mainExecutor.isTerminated()) {
                Thread.sleep(60000);
                //System.out.println(Thread.currentThread().getName()+" 等待主线程关闭...");
            }


        }else{

            //设置好初始路径
            String folder=folderpath + File.separator + foldername;
            //下载单个m3u8文件
            downloadOneM3u8(urls.get(0),folder,"任务1",1);
            //合并完成后删除缓存文件
            delAllFile(new File(folder));

        }

        System.out.println("下载完成...");
        }


    private static void readByTxt(List<String> urls) throws IOException {
        File m3u8File=new File(m3u8TxtFile);
        if(m3u8File.exists()&&m3u8File.isFile()){
            BufferedReader  br=new BufferedReader(new FileReader(m3u8File));
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法，一次读一行
                System.out.println(s);
                urls.add(s);
            }
            br.close();;
        }
    }


    private static void downloadOneM3u8(String url,String folder,String threadName,int i) throws IOException, InterruptedException {
        File dir = new File(folder);
        //防止文件夹里有其他文件，做好分类
        dir.deleteOnExit();
       /* if (dir.exists()) {

            System.out.println("文件夹："+folderpath+"已存在！");
            return;
        }*/
        //获取到地址里面的m3u8文件名称
        final String m3u8name = url.substring(url.lastIndexOf("/"), url.toLowerCase().indexOf(".m3u8", url.lastIndexOf("/"))) + ".m3u8";

        //先将m3u8文件保存到本地，以便不用合成也能播放对应的视频
        M3U8Util.saveM3u8File(folder, url, m3u8name, RETRY_TIMES);
        //解析M3U8地址为对象
        M3U8 m3u8 = M3U8Util.parseM3U8Content(folder, m3u8name, url, RETRY_TIMES);

        //根据M3U8对象获取时长
        float duration = M3U8Util.getDuration(m3u8);
        System.out.println(Thread.currentThread().getName()+" 时长: " + ((int) duration / 60) + "分" + (int) duration % 60 + "秒");
        ExecutorService executor=initThread(20,threadName);

        //根据M3U8对象下载视频段
        M3U8Util.download(m3u8, executor,RETRY_TIMES);

        //关闭线程池
        executor.shutdown();
        System.out.println(Thread.currentThread().getName()+" 等待下载中...");
        while (!executor.isTerminated()) {
            Thread.sleep(50000);
            //System.out.println(Thread.currentThread().getName()+" 等待子线程关闭...");
        }

        //合并文件
        M3U8Util.merge(m3u8, tofile,i);

        System.out.println(Thread.currentThread().getName()+" 下载完成，文件在: " + folder);
    }

    public static ExecutorService initThread(int max,String threadName) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat(threadName+"-%d").build();
        return new ThreadPoolExecutor(20, 40, 600, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), namedThreadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    private static void excludeLog() {
        Set<String> loggers = new HashSet<>(Arrays.asList("org.apache.http", "groovyx.net.http", "com.around.test.SSLClient"));

        for (String log : loggers) {
            Logger logger = (Logger) LoggerFactory.getLogger(log);
            logger.setLevel(Level.INFO);
        }
    }

    /**
     * 获取剪切板里面的文字数据
     *
     * @return
     * @Title: getSysClipboardText
     * @version v1.0.0
     * @author guojin
     * @date 2019年2月15日上午11:24:34
     */
    public static String getSysClipboardText() {
        String ret = "";
        Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
        // 获取剪切板中的内容
        Transferable clipTf = sysClip.getContents(null);

        if (clipTf != null) {
            // 检查内容是否是文本类型
            if (clipTf.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                try {
                    ret = (String) clipTf
                            .getTransferData(DataFlavor.stringFlavor);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return ret;
    }


    public static void delAllFile(File dir) {
        if(!dir.exists()){
            return;
        }
        System.gc();

        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            for (File file : files) {
                delAllFile(file);
            }
        }
        dir.delete();
    }


}
